@tool
class_name Attribute
extends Resource

#region 属性
@export var attribute_name:String
## 原始数值（保持不变）
@export var base_value:CompositeProperty
## 依赖属性
@export var derived_from_attributes:Array[String]
## 计算后的结果
var current_value = 0.0:
	set = setter_current_value
## 计算公式，如果为空或""直接返回base_value
var calc_expression:String = ""
var modifiers:Array[AttributeModifier] = []
var attribute_set:AttributeSet
var connect_obj_dict:Dictionary = {}
#endregion
#region 字段
#endregion
#region 信号
signal attribute_changed(attribute:Attribute,old_value,new_value)
#endregion

#region Setter
func setter_current_value(v):
	var old_value = current_value
	current_value = v
	attribute_changed.emit(self,old_value,current_value)
#endregion
#region 基类方法
#endregion
#region 公共方法
func init(attribute_set:AttributeSet,args:Dictionary = {}):
	self.attribute_set = attribute_set
	var base_value_temp = args.get("BaseValue")
	if base_value_temp:
		base_value = base_value_temp
	current_value = base_value.get_value(self.attribute_set.init_args.get("Entity"))
	## 绑定base_value修改事件
	connect_obj_dict.set(base_value.property_changed,ConnectObj.new(base_value.property_changed,[_on_property_changed]))
	## 绑定修改事件
	for attribute_name in derived_from_attributes:
		var attribute = self.attribute_set.find_attribute(attribute_name)
		if attribute:
			connect_obj_dict.set(attribute.attribute_changed,ConnectObj.new(attribute.attribute_changed,[_on_derived_from_attribute_changed]))
func get_base_value():
	return base_value.get_value(self.attribute_set.init_args.get("Entity"))
func get_value():
	return current_value
func recompute_attribute():
	current_value = _compute_value()
## 强行设置base_value，必须和初始化时的类型一致
func force_set_base_value(value):
	base_value.set_value(value)
	recompute_attribute()
func apply_modifier(modifier:AttributeModifier):
	if modifier.unique_id:
		var modifiers_by_unique_id = get_modifiers_by_unique_id(modifier.unique_id)
		Utils.array_for_each(modifiers_by_unique_id,func(modifier):remove_modifier(modifier))
	modifiers.append(modifier)
	recompute_attribute()
func remove_modifier(modifier:AttributeModifier):
	modifiers.erase(modifier)
	recompute_attribute() 
func remove_modifier_by_tag(tag:String = "Default"):
	var modifiers = get_modifiers_by_tag(tag)
	for modifier in modifiers:
		remove_modifier(modifier)
	recompute_attribute()
func destroy():
	for connect_obj:ConnectObj in connect_obj_dict.values():
		if connect_obj:
			connect_obj.disconnect_all()
## 获得指定Unique_id的Modifier
func get_modifiers_by_unique_id(unique_id:String) -> Array[AttributeModifier]:
	return modifiers.filter(func(x:AttributeModifier):return x.unique_id == unique_id)
## 获得指定Tag的Modifier
func get_modifiers_by_tag(tag:String = "Default") -> Array[AttributeModifier]:
	return modifiers.filter(func(x:AttributeModifier):return x.tag == tag)
#endregion
#region 私有方法
## 数值计算
func _compute_value():
	var compute_result = _evaluate_expression(calc_expression,get_base_value())
	## 应用修改器
	for modifier in modifiers:
		compute_result = modifier.operate(compute_result)
	return compute_result
func _evaluate_expression(expression:String,default_value:Variant) -> Variant:
	if not expression:
		return default_value
	var ex = Expression.new()
	var input_names = attribute_set.attributes_runtime_dict.keys()
	var result = ex.parse(expression,input_names)
	if result != OK:
		push_error("解析{0}错误：{1}".format([expression,ex.get_error_text()]))
		return default_value
	var input_values:Array = []
	for input_name in input_names:
		input_values.append(attribute_set.attributes_runtime_dict[input_name])
	var excute_result = ex.execute(input_values,self,false)
	if ex.has_execute_failed():
		push_error("执行{0}错误：{1}".format([expression,ex.get_error_text()]))
		return default_value
	return excute_result
func _on_property_changed(composite_property:CompositeProperty):
	recompute_attribute()
func _on_derived_from_attribute_changed(attribute:Attribute,old_value,new_value):
	recompute_attribute()
#endregion
#region 生命周期函数
func _init() -> void:
	if Engine.is_editor_hint():
		resource_local_to_scene = true
func _get_property_list() -> Array[Dictionary]:
	var properties:Array[Dictionary] = []
	properties.append({
		"name": "calc_expression",
		"type": TYPE_STRING,
		"usage": PROPERTY_USAGE_DEFAULT,
		"hint": PROPERTY_HINT_EXPRESSION
	})
	return properties
#func _notification(what: int) -> void:
	#if what == NOTIFICATION_PREDELETE:
		#destroy()
#endregion
